﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using Microsoft.SharePoint;
using System.Reflection;
using Microsoft.SharePoint.Administration;
using System.Diagnostics;
using System.Collections;

namespace WorkflowTaskReplicator
{
    public static class Helper
    {
        public static readonly SPContentTypeId WorkflowTaskReplicatorContentTypeId = new SPContentTypeId("0x01080100541D62C0985F4D6F8928CCF0AE3327E9");
        internal static readonly string TaskItemEventReceiverClassName = "WorkflowTaskReplicator.TaskItemEventReceiver";
        internal static readonly string DiagnosticsLoggingCategory = "WorkflowTaskReplicator";

        internal static readonly string Task_Title = "Title";
        internal static readonly string Task_PercentComplete = "PercentComplete";
        internal static readonly string Task_Status = "Status";

        internal static readonly string WorkflowTaskReplicator_AllSubTasksMustBeCompleted = "WorkflowTaskReplicator_AllSubTasksMustBeCompleted";
        internal static readonly string WorkflowTaskReplicator_ContentType = "WorkflowTaskReplicator_ContentType";
        internal static readonly string WorkflowTaskReplicator_SubTasks = "WorkflowTaskReplicator_SubTasks";
        internal static readonly string WorkflowTaskReplicator_AssignedTo = "WorkflowTaskReplicator_AssignedTo";
        internal static readonly string WorkflowTaskReplicator_AssignedToStringForWorkflow = "WorkflowTaskReplicator_AssignedToStringForWorkflow";
        internal static readonly string WorkflowTaskReplicator_Contributors = "WorkflowTaskReplicator_Contributors";
        internal static readonly string WorkflowTaskReplicator_ContributorsStringForWorkflow = "WorkflowTaskReplicator_ContributorsStringForWorkflow";
        internal static readonly string WorkflowTaskReplicator_Owners = "WorkflowTaskReplicator_Owners";
        internal static readonly string WorkflowTaskReplicator_OwnersStringForWorkflow = "WorkflowTaskReplicator_OwnersStringForWorkflow";
        internal static readonly string WorkflowTaskReplicator_Readers = "WorkflowTaskReplicator_Readers";
        internal static readonly string WorkflowTaskReplicator_ReadersStringForWorkflow = "WorkflowTaskReplicator_ReadersStringForWorkflow";
        internal static readonly string WorkflowTaskReplicator_SubTaskMetaData = "WorkflowTaskReplicator_SubTaskMetaData";

        /// <summary>
        /// Serializes the sub tasks.
        /// </summary>
        /// <param name="subTasks">The sub tasks.</param>
        /// <returns></returns>
        internal static string SerializeSubTasks(Dictionary<Guid, bool> subTasks)
        {
            //Try to serialize a list of tasks
            if (subTasks != null)
            {
                List<string> sTasks = new List<string>();

                foreach(KeyValuePair<Guid, bool> kvp in subTasks)
                    sTasks.Add(String.Concat(kvp.Key, ";", kvp.Value));

                JavaScriptSerializer serializer = new JavaScriptSerializer();

                try
                {
                    return serializer.Serialize(sTasks);
                }
                catch(Exception ex)
                {
                    Log(String.Format("Error serializing sub tasks collection.\n\n{0}\n\n{1}", ex.Message, ex.StackTrace));
                }
            }

            return String.Empty;
        }

        /// <summary>
        /// Deserializes the sub tasks.
        /// </summary>
        /// <param name="subTasksString">The sub tasks string.</param>
        /// <returns></returns>
        internal static Dictionary<Guid, bool> DeserializeSubTasks(string subTasksString)
        {
            //Try to deserialize a list of tasks
            if (!String.IsNullOrEmpty(subTasksString))
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                List<string> sTasks = null;

                try
                {
                    sTasks = serializer.Deserialize<List<string>>(subTasksString);

                    if (sTasks != null)
                    {
                        Dictionary<Guid, bool> tasks = new Dictionary<Guid, bool>();
                        string[] splittedTask;

                        foreach (string s in sTasks)
                        {
                            splittedTask = s.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                            if (splittedTask.Length == 2)
                                tasks.Add(new Guid(splittedTask[0]), bool.Parse(splittedTask[1]));
                        }

                        return tasks;
                    }
                }
                catch(Exception ex)
                {
                    Log(String.Format("Error deserializing sub tasks from string.\n\n{0}\n\n{1}\n\n{2}", subTasksString, ex.Message, ex.StackTrace));
                }
            }

            return new Dictionary<Guid, bool>();
        }

        /// <summary>
        /// Serializes the sub task meta data.
        /// </summary>
        /// <param name="metaData">The meta data.</param>
        /// <returns></returns>
        internal static string SerializeSubTaskMetaData(Dictionary<string, string> metaData)
        {
            //Try to serialize a list meta data
            if (metaData != null)
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();

                try
                {
                    return serializer.Serialize(metaData);
                }
                catch (Exception ex)
                {
                    Log(String.Format("Error serializing sub task meta data.\n\n{0}\n\n{1}", ex.Message, ex.StackTrace));
                }
            }

            return String.Empty;
        }

        /// <summary>
        /// Deserializes the sub task meta data.
        /// </summary>
        /// <param name="metaDataString">The meta data string.</param>
        /// <returns></returns>
        internal static Dictionary<string, string> DeserializeSubTaskMetaData(string metaDataString)
        {
            //Try to deserialize a list of task meta data
            if (!String.IsNullOrEmpty(metaDataString))
            {
                JavaScriptSerializer serializer = new JavaScriptSerializer();

                try
                {
                    return serializer.Deserialize<Dictionary<string, string>>(metaDataString);
                }
                catch (Exception ex)
                {
                    Log(String.Format("Error deserializing sub task meta data from string.\n\n{0}\n\n{1}\n\n{2}", metaDataString, ex.Message, ex.StackTrace));
                }
            }

            return new Dictionary<string, string>();
        }
        
        /// <summary>
        /// Finds the parent task.
        /// </summary>
        /// <param name="list">The list.</param>
        /// <param name="currentTaskId">The current task id.</param>
        /// <returns></returns>
        internal static SPListItem FindParentTask(SPList list, Guid subTaskId)
        {
            SPListItem returnValue = null;

            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (SPSite eSite = new SPSite(list.ParentWeb.Site.ID))
                {
                    using (SPWeb eWeb = eSite.OpenWeb(list.ParentWeb.ID))
                    {
                        SPList eList = eWeb.Lists[list.ID];

                        //Find WorkflowTaskReplicator list content type id
                        SPContentTypeId ctId = eList.ContentTypes.BestMatch(WorkflowTaskReplicatorContentTypeId);

                        if (ctId.IsChildOf(WorkflowTaskReplicatorContentTypeId))
                        {
                            //Find the parent task
                            SPQuery query = new SPQuery();

                            query.QueryThrottleMode = SPQueryThrottleOption.Override;
                            query.Query = String.Format(@"<Where>
                                                          <And>
                                                             <And>
                                                                <Neq>
                                                                    <FieldRef Name='PercentComplete'/>
                                                                    <Value Type='Text'>1</Value>
                                                                </Neq>
                                                                <Eq>
                                                                    <FieldRef Name='ContentTypeId'/>
                                                                    <Value Type='Text'>{0}</Value>
                                                                </Eq>
                                                             </And>
                                                             <Contains>
                                                                <FieldRef Name='WorkflowTaskReplicator_SubTasks'/>
                                                                <Value Type='Note'>{1}</Value>
                                                             </Contains>
                                                          </And>
                                                       </Where>", ctId, subTaskId);

                            SPListItemCollection items = eList.GetItems(query);

                            if (items.Count > 0)
                                returnValue = items[0];
                            else
                                Log(String.Format("Parent task not found on list '{0}' for sub task with id '{1}'.", list.RootFolder.Url, subTaskId));
                        }
                        else
                            Log(String.Format("WorkflowTaskReplicator content type not found on list '{0}'.", list.RootFolder.Url));
                    }
                }
            });

            return returnValue;
        }

        /// <summary>
        /// Adds the list item event receivers.
        /// </summary>
        /// <param name="list">The list.</param>
        internal static void AddListItemEventReceivers(SPList list)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (SPSite eSite = new SPSite(list.ParentWeb.Site.ID))
                {
                    using (SPWeb eWeb = eSite.OpenWeb(list.ParentWeb.ID))
                    {
                        SPList eList = eWeb.Lists[list.ID];

                        bool foundAdding = false;
                        bool foundAdded = false;
                        bool foundUpdating = false;
                        bool foundUpdated = false;
                        bool foundDeleting = false;
                        bool foundDeleted = false;
                        string assemblyName = Assembly.GetExecutingAssembly().FullName;

                        //Loop all event receivers on the content type
                        foreach (SPEventReceiverDefinition erd in eList.EventReceivers)
                        {
                            //Check if our event receivers have been registered
                            if (erd.Assembly == assemblyName && erd.Class == TaskItemEventReceiverClassName)
                            {
                                switch (erd.Type)
                                {
                                    case SPEventReceiverType.ItemAdding:
                                        foundAdding = true;
                                        break;
                                    case SPEventReceiverType.ItemAdded:
                                        foundAdded = true;
                                        break;
                                    case SPEventReceiverType.ItemUpdating:
                                        foundUpdating = true;
                                        break;
                                    case SPEventReceiverType.ItemUpdated:
                                        foundUpdated = true;
                                        break;
                                    case SPEventReceiverType.ItemDeleting:
                                        foundDeleting = true;
                                        break;
                                    case SPEventReceiverType.ItemDeleted:
                                        foundDeleted = true;
                                        break;
                                }
                            }

                            if (foundAdding && foundAdded && foundUpdating && foundUpdated && foundDeleting && foundDeleted)
                                break;
                        }

                        //Register missing event receivers
                        if (!foundAdding)
                        {
                            eList.EventReceivers.Add(SPEventReceiverType.ItemAdding, assemblyName, TaskItemEventReceiverClassName);
                            Log(String.Format("Event receiver ItemAdding '{0}' added to list '{1}'.", TaskItemEventReceiverClassName, list.RootFolder.Url));
                        }

                        if (!foundAdded)
                        {
                            eList.EventReceivers.Add(SPEventReceiverType.ItemAdded, assemblyName, TaskItemEventReceiverClassName);
                            Log(String.Format("Event receiver ItemAdded '{0}' added to list '{1}'.", TaskItemEventReceiverClassName, list.RootFolder.Url));
                        }

                        if (!foundUpdating)
                        {
                            eList.EventReceivers.Add(SPEventReceiverType.ItemUpdating, assemblyName, TaskItemEventReceiverClassName);
                            Log(String.Format("Event receiver ItemUpdating '{0}' added to list '{1}'.", TaskItemEventReceiverClassName, list.RootFolder.Url));
                        }

                        if (!foundUpdated)
                        {
                            eList.EventReceivers.Add(SPEventReceiverType.ItemUpdated, assemblyName, TaskItemEventReceiverClassName);
                            Log(String.Format("Event receiver ItemUpdated '{0}' added to list '{1}'.", TaskItemEventReceiverClassName, list.RootFolder.Url));
                        }

                        if (!foundDeleting)
                        {
                            eList.EventReceivers.Add(SPEventReceiverType.ItemDeleting, assemblyName, TaskItemEventReceiverClassName);
                            Log(String.Format("Event receiver ItemDeleting '{0}' added to list '{1}'.", TaskItemEventReceiverClassName, list.RootFolder.Url));
                        }

                        if (!foundDeleted)
                        {
                            eList.EventReceivers.Add(SPEventReceiverType.ItemDeleted, assemblyName, TaskItemEventReceiverClassName);
                            Log(String.Format("Event receiver ItemDeleted '{0}' added to list '{1}'.", TaskItemEventReceiverClassName, list.RootFolder.Url));
                        }

                        if (!foundAdding || !foundAdded || !foundUpdating || !foundUpdated || !foundDeleting || !foundDeleted)
                            eList.Update();
                    }
                }
            });
        }

        /// <summary>
        /// Removes the list item event receivers.
        /// </summary>
        /// <param name="list">The list.</param>
        internal static void RemoveListItemEventReceivers(SPList list)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (SPSite eSite = new SPSite(list.ParentWeb.Site.ID))
                {
                    using (SPWeb eWeb = eSite.OpenWeb(list.ParentWeb.ID))
                    {
                        SPList eList = eWeb.Lists[list.ID];

                        bool update = false;
                        string assemblyName = Assembly.GetExecutingAssembly().FullName;

                        //Loop all event receivers on the content type
                        for (int i = eList.EventReceivers.Count - 1; i >= 0; i--)
                        {
                            if (eList.EventReceivers[i].Assembly == assemblyName && eList.EventReceivers[i].Class == TaskItemEventReceiverClassName)
                            {
                                Log(String.Format("Event receiver {0} '{1}' removed from list '{2}'.", eList.EventReceivers[i].Type, TaskItemEventReceiverClassName, list.RootFolder.Url));

                                eList.EventReceivers[i].Delete();
                                update = true;
                            }
                        }

                        if (update)
                            eList.Update();
                    }
                }
            });
        }

        /// <summary>
        /// List item is task.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        internal static bool ListItemIsTask(SPListItem item)
        {
            if(item != null)
                return item.ContentTypeId.IsChildOf(SPBuiltInContentTypeId.Task);

            return false;
        }

        /// <summary>
        /// Sets the list item permissions.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="contributor">The contributor.</param>
        /// <param name="additionalContributors">The additional contributors.</param>
        /// <param name="additionalOwners">The additional owners.</param>
        /// <param name="additionalReaders">The additional readers.</param>
        internal static void SetListItemPermissions(SPListItem item, SPUser contributor, List<SPUser> additionalContributors, List<SPUser> additionalOwners, List<SPUser> additionalReaders)
        {
            //Break role inheritance
            item.BreakRoleInheritance(false);

            Log(String.Format("Permissions removed on list item '{0}'.", item.UniqueId));

            SPRoleDefinition roleDef;
            SPRoleAssignment roleAssign;
            bool update = false;

            if (contributor != null)
            {
                //Add contributor rights (assigned to)
                roleDef = item.Web.RoleDefinitions.GetByType(SPRoleType.Contributor);
                roleAssign = new SPRoleAssignment(contributor);

                roleAssign.RoleDefinitionBindings.Add(roleDef);
                item.RoleAssignments.Add(roleAssign);

                Log(String.Format("Custom permissions (contributor) set on list item '{0}'.", item.UniqueId));
                update = true;
            }

            //Add additional contributor rights
            if (additionalContributors != null)
            {
                roleDef = item.Web.RoleDefinitions.GetByType(SPRoleType.Contributor);

                foreach (SPUser user in additionalContributors)
                {
                    roleAssign = new SPRoleAssignment(user);
                    roleAssign.RoleDefinitionBindings.Add(roleDef);
                    item.RoleAssignments.Add(roleAssign);
                }

                Log(String.Format("Custom permissions (additional contributors) set on list item '{0}'.", item.UniqueId));
                update = true;
            }

            //Add owner rights
            if (additionalOwners != null)
            {
                roleDef = item.Web.RoleDefinitions.GetByType(SPRoleType.Administrator);
                foreach (SPUser user in additionalOwners)
                {
                    roleAssign = new SPRoleAssignment(user);
                    roleAssign.RoleDefinitionBindings.Add(roleDef);
                    item.RoleAssignments.Add(roleAssign);
                }

                Log(String.Format("Custom permissions (additional owners) set on list item '{0}'.", item.UniqueId));
                update = true;
            }

            //Add reader rights
            if (additionalReaders != null)
            {
                roleDef = item.Web.RoleDefinitions.GetByType(SPRoleType.Reader);
                foreach (SPUser user in additionalReaders)
                {
                    roleAssign = new SPRoleAssignment(user);
                    roleAssign.RoleDefinitionBindings.Add(roleDef);
                    item.RoleAssignments.Add(roleAssign);
                }

                Log(String.Format("Custom permissions (additional readers) set on list item '{0}'.", item.UniqueId));
                update = true;
            }

            if(update)
                item.UpdateOverwriteVersion();
        }

        /// <summary>
        /// Gets the sub tasks info.
        /// </summary>
        /// <param name="parentTask">The parent task.</param>
        /// <returns></returns>
        public static Dictionary<Guid, bool> GetSubTasksInfo(SPListItem parentTask)
        {
            //Public method to parse sub tasks info from parent task

            //Check if parent tasks is WorkflowTaskReplicator
            if (parentTask.ContentTypeId.IsChildOf(WorkflowTaskReplicatorContentTypeId))
            {
                return DeserializeSubTasks((string)parentTask[WorkflowTaskReplicator_SubTasks]);
            }
            else
                Log(String.Format("List item '{0}' doesn't implement the WorkflowTaskReplicator content type.", parentTask.UniqueId));

            return new Dictionary<Guid, bool>();
        }

        /// <summary>
        /// Logs the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        internal static void Log(string message)
        {
            message = String.Concat((new StackFrame(1)).GetMethod().Name, ": ", message);
            SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory(DiagnosticsLoggingCategory, TraceSeverity.Monitorable, EventSeverity.Information), TraceSeverity.Monitorable, message, null);
        }

        /// <summary>
        /// Sets the user value columns.
        /// </summary>
        /// <param name="item">The item.</param>
        internal static void SetUserValueColumns(SPListItem item)
        {
            SPFieldUserValueCollection assignees = (SPFieldUserValueCollection)item[Helper.WorkflowTaskReplicator_AssignedTo];
            SPFieldUserValueCollection contributors = (SPFieldUserValueCollection)item[Helper.WorkflowTaskReplicator_Contributors];
            SPFieldUserValueCollection owners = (SPFieldUserValueCollection)item[Helper.WorkflowTaskReplicator_Owners];
            SPFieldUserValueCollection readers = (SPFieldUserValueCollection)item[Helper.WorkflowTaskReplicator_Readers];
            bool update = false;

            //If user value field values to cannot be retrieved, check the string values
            if (assignees == null)
            {
                try
                {
                    assignees = new SPFieldUserValueCollection(item.Web, (string)item[Helper.WorkflowTaskReplicator_AssignedToStringForWorkflow]);
                    update = true;
                }
                catch
                {
                    Log(String.Format("Error parsing assignees from string on item '{0}'.", item.UniqueId));
                }
            }

            if (contributors == null)
            {
                try
                {
                    contributors = new SPFieldUserValueCollection(item.Web, (string)item[Helper.WorkflowTaskReplicator_ContributorsStringForWorkflow]);
                    update = true;
                }
                catch
                {
                    Log(String.Format("Error parsing contributors from string on item '{0}'.", item.UniqueId));
                }
            }

            if (owners == null)
            {
                try
                {
                    owners = new SPFieldUserValueCollection(item.Web, (string)item[Helper.WorkflowTaskReplicator_OwnersStringForWorkflow]);
                    update = true;
                }
                catch
                {
                    Log(String.Format("Error parsing owners from string on item '{0}'.", item.UniqueId));
                }
            }

            if (readers == null)
            {
                try
                {
                    readers = new SPFieldUserValueCollection(item.Web, (string)item[Helper.WorkflowTaskReplicator_ReadersStringForWorkflow]);
                    update = true;
                }
                catch
                {
                    Log(String.Format("Error parsing readers from string on item '{0}'.", item.UniqueId));
                }
            }

            //Update item
            if (update)
            {
                item[Helper.WorkflowTaskReplicator_AssignedTo] = assignees;
                item[Helper.WorkflowTaskReplicator_Contributors] = contributors;
                item[Helper.WorkflowTaskReplicator_Owners] = owners;
                item[Helper.WorkflowTaskReplicator_Readers] = readers;
                item.UpdateOverwriteVersion();
            }
        }

        /// <summary>
        /// Converts the SP field user value collection.
        /// </summary>
        /// <param name="users">The users.</param>
        /// <returns></returns>
        internal static List<SPUser> ConvertSPFieldUserValueCollection(SPFieldUserValueCollection users)
        {
            List<SPUser> returnValue = new List<SPUser>();

            if (users != null)
            {
                foreach (SPFieldUserValue uv in users)
                    returnValue.Add(uv.User);
            }

            return returnValue;
        }

        /// <summary>
        /// Converts the list SP user.
        /// </summary>
        /// <param name="users">The users.</param>
        /// <returns></returns>
        internal static SPFieldUserValueCollection ConvertListSPUser(List<SPUser> users)
        {
            SPFieldUserValueCollection returnValue = new SPFieldUserValueCollection();

            if (users != null)
            {
                foreach (SPUser u in users)
                    returnValue.Add(new SPFieldUserValue(u.ParentWeb, u.ID, u.Name));
            }

            return returnValue;
        }

        /// <summary>
        /// Sets the task extended properties.
        /// </summary>
        /// <param name="extendedProperties">The extended properties.</param>
        /// <param name="allSubTasksMustBeCompleted">if set to <c>true</c> [all sub tasks must be completed].</param>
        /// <param name="subTaskContentTypeId">The sub task content type id.</param>
        /// <param name="assignedTo">The assigned to.</param>
        /// <param name="additionalContributors">The additional contributors.</param>
        /// <param name="additionalOwners">The additional owners.</param>
        /// <param name="additionalReaders">The additional readers.</param>
        /// <param name="subTaskColumnNameValue">The sub task column name value.</param>
        public static void SetTaskExtendedProperties(Hashtable extendedProperties, bool allSubTasksMustBeCompleted, SPContentTypeId subTaskContentTypeId, List<SPUser> assignedTo, List<SPUser> additionalContributors, List<SPUser> additionalOwners, List<SPUser> additionalReaders, Dictionary<string, string> subTaskColumnNameValue)
        {
            //Set all required metadata throught thask extended properties
            extendedProperties.Add(WorkflowTaskReplicator_AllSubTasksMustBeCompleted, allSubTasksMustBeCompleted ? "True" : "False");
            extendedProperties.Add(WorkflowTaskReplicator_ContentType, subTaskContentTypeId.ToString());
            extendedProperties.Add(WorkflowTaskReplicator_AssignedToStringForWorkflow, ConvertListSPUser(assignedTo).ToString());
            extendedProperties.Add(WorkflowTaskReplicator_ContributorsStringForWorkflow, ConvertListSPUser(additionalContributors).ToString());
            extendedProperties.Add(WorkflowTaskReplicator_OwnersStringForWorkflow, ConvertListSPUser(additionalOwners).ToString());
            extendedProperties.Add(WorkflowTaskReplicator_ReadersStringForWorkflow, ConvertListSPUser(additionalReaders).ToString());
            extendedProperties.Add(WorkflowTaskReplicator_SubTaskMetaData, SerializeSubTaskMetaData(subTaskColumnNameValue));
        }

        /// <summary>
        /// Determines whether [is workflow task replicator CT attached to list] [the specified list].
        /// </summary>
        /// <param name="list">The list.</param>
        /// <returns>
        ///   <c>true</c> if [is workflow task replicator CT attached to list] [the specified list]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsWorkflowTaskReplicatorCTAttachedToList(SPList list)
        {
            return list.ContentTypes.BestMatch(WorkflowTaskReplicatorContentTypeId).IsChildOf(WorkflowTaskReplicatorContentTypeId);
        }

        /// <summary>
        /// Attaches the workflow task replicator CT on list.
        /// </summary>
        /// <param name="list">The list.</param>
        public static void AttachWorkflowTaskReplicatorCTOnList(SPList list)
        {
            //Check if content type is already present
            if (!IsWorkflowTaskReplicatorCTAttachedToList(list))
            {
                SPSecurity.RunWithElevatedPrivileges(delegate
                {
                    using (SPSite eSite = new SPSite(list.ParentWeb.Site.ID))
                    {
                        using (SPWeb eWeb = eSite.OpenWeb(list.ParentWeb.ID))
                        {
                            SPList eList = eWeb.Lists[list.ID];
                            SPContentType ct = eWeb.ContentTypes[WorkflowTaskReplicatorContentTypeId];

                            if (ct != null)
                            {
                                //Attach our content type to the list
                                eList.ContentTypes.Add(ct);
                                eList.Update();

                                Log(String.Format("WorkflowTaskReplicator content type has been added to list '{0}'.", eList.RootFolder.Url));

                                //Add our event receivers to the list
                                AddListItemEventReceivers(list);
                            }
                            else
                                Log(String.Format("WorkflowTaskReplicator content type not found on web '{0}'.", eWeb.Url));
                        }
                    }
                });
            }
            else
                Log(String.Format("WorkflowTaskReplicator content type is already attached to '{0}'.", list.RootFolder.Url));
        }

        /// <summary>
        /// Detaches the workflow task replicator CT from list.
        /// </summary>
        /// <param name="list">The list.</param>
        public static void DetachWorkflowTaskReplicatorCTFromList(SPList list)
        {
            //Check if content type is present
            if (IsWorkflowTaskReplicatorCTAttachedToList(list))
            {
                SPSecurity.RunWithElevatedPrivileges(delegate
                {
                    using (SPSite eSite = new SPSite(list.ParentWeb.Site.ID))
                    {
                        using (SPWeb eWeb = eSite.OpenWeb(list.ParentWeb.ID))
                        {
                            SPList eList = eWeb.Lists[list.ID];
                            SPContentType ct = eList.ContentTypes[eList.ContentTypes.BestMatch(WorkflowTaskReplicatorContentTypeId)];

                            ct.Delete();
                            eList.Update();

                            Log(String.Format("WorkflowTaskReplicator content type has been removed from list '{0}'.", eList.RootFolder.Url));
                        }
                    }
                });
            }
            else
                Log(String.Format("WorkflowTaskReplicator content type is not attached to '{0}'.", list.RootFolder.Url));
        }
    }
}